#ifndef __FAISS_TOOL_H__
#define __FAISS_TOOL_H__
#include <cfloat>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <time.h>
#include <omp.h>
#include <stdio.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/flann/flann.hpp"
#include "boost/algorithm/string.hpp"
#include "google/protobuf/text_format.h"

#include <faiss/IndexFlat.h>
#include <faiss/index_io.h>
#include <pthread.h>
#include <unistd.h>

using namespace cv;
using namespace std;



string faiss_retrival_return(faiss::IndexFlatIP index,vector<bool> is_image,vector<string> query_name, vector<string> retrival_name, int queryNum, int retrivalNum, Mat feat_query, Mat feat_retrival, float threshold)
{
      		//float* queryArr=new float[feat_query.rows*feat_query.cols];
                stringstream ss;
		float* queryArr=(float*)feat_query.data;
//                cout<<queryArr[4]<<" "<<queryArr[7]<<endl;
		int TOPK = 1;
		long *I = new long[TOPK * queryNum];
		float *D = new float[TOPK * queryNum];
		index.search(queryNum, queryArr, TOPK, D, I);
		//printf("I=\n");
		/*for(int i = 0; i < 5; i++) {
				for(int j = 0; j < TOPK; j++)
					printf("%5ld ", I[i * TOPK + j]);
            printf("\n");
		}*/

		for(int i = 0; i < queryNum; i++)
		{
			 cout << endl << "query image id: "<< i << ", name: " << query_name[i] << endl;
				 
			for(int j = 0; j < TOPK; j++)
			{
				int t = I[i*TOPK+j];
				if (retrival_name[t] == "43c9c4b57e6c7e1638438c722b7fa927" || retrival_name[t] == "47b9fbd0d7258bbf45cc6c2fb9cd61e0" || retrival_name[t] == "8047b4e3cc6856335d15a4f96cd2e6a7" || retrival_name[t] == "982bcb887525dec8e8f84bdf08fd3b9f" || retrival_name[t] == "91b19e7d8c61cf0ae2ad97d1b7a962c4"|| retrival_name[t] == "e6cd78f4511152c9bc89ca185374ca7b" || retrival_name[t] == "e0fc1347f0245e75266177becc99b47c" || retrival_name[t] == "e5f3b10fe7a6e41dcae9d7faf571637c" || retrival_name[t] == "e09fcac5602b45916eb87602daf27c54" || retrival_name[t] == "c670e9e675272142e8c0ad9613e66192" || retrival_name[t] == "de03657e1c139cc39816a43704dd2932" || retrival_name[t] == "4fabfc3b2f28d1b6f8c3b617d617d249" || retrival_name[t] == "6e38d392766e64f8f75edde0c6776c98" || retrival_name[t] == "e4bb7fed959444c4d0b297ac21355f27" || retrival_name[t] == "874669bc3a775a0db66055250448ee8e" || retrival_name[t] == "9c49ef9bfad450c34ff61bf08113d3b0")
				{
					if(D[i*TOPK+j] > 0.93 && is_image[i])

						ss << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j] << endl;

				}			

				else if(retrival_name[t] == "f2ac2484802d16ad761226c8db5f3a6d" || retrival_name[t] == "31be1bb7daedbd2098be93777da4e80a" || retrival_name[t] == "a1aac9e96c5c49dfabb6be5f428d100d" || retrival_name[t] == "eb886f4a465de7a32d7f8e353a73f99c" || retrival_name[t]== "a25967217571e914a97449b4fbeef812" || retrival_name[t] == "a1aac9e96c5c49dfabb6be5f428d100d" || retrival_name[t] == "252873714b9a08cdba77e27695707f38" || retrival_name[t] == "764115d430662f7943eaaec7db12dc3e" || retrival_name[t] == "11be01413909989811131a0193894914" || retrival_name[t] =="12488ec380d041534f82dd6cfeebccdf" || retrival_name[t] == "9f71e451fd044385478d2109ccb245e6" || retrival_name[t] == "b32a1a5cb98f07e3fb7c7c2542ed550d" || retrival_name[t] == "1d1339e7ee191ec94442595a5a5211e1" || retrival_name[t] == "26b69573ba74e9803319913f80171c61" || retrival_name[t] == "826e0ed562c5e1b842065ea59aef046b" || retrival_name[t] == "786e9d7d71a397bdf4eb5225a358a603" || retrival_name[t] == "830ab0020f63c2847280f5d0cb924c3e" ) 
				{	
					if(D[i*TOPK+j] > 0.93 && is_image[i])
					{
						ss << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j] << endl;
						cout << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j] << endl;
					}
				}	
				else
				{
					if (D[i*TOPK+j] > threshold && is_image[i])
					{
						ss << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j] << endl;
						cout << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j] << endl;
					}		
				}
			 }
                     
        }
        time_t t6 = time(NULL);
		//delete []queryArr;
        delete [] I;
        delete [] D;
		return ss.str();
}



class Indexsingle
{

private:
    static boost::shared_ptr<Indexsingle> p;
	static pthread_mutex_t mutex;
	static pthread_mutex_t mutex_update;
	int featDim,retrivalNum;
	faiss::IndexFlatIP index;
	vector<string> retrival_name;
	vector<string> retrival_path;
	Mat feat_retrival;
	

	
	
public:
    Indexsingle();
    static boost::shared_ptr<Indexsingle> initance();
	static void update(string sampleFile,string featureFile);
	string search(vector<bool> is_image,vector<string> query_name,  int queryNum, Mat feat_query,  float threshold);
	void appendFeature(string sampleFile,string featureFile);
	void save();
	//Indexsingle();
};
boost::shared_ptr<Indexsingle> Indexsingle::p=NULL;
//faiss::IndexFlatIP* Indexsingle::index=NULL;
//vector<string> Indexsingle::retrival_name;
//Mat Indexsingle::feat_retrival;
pthread_mutex_t Indexsingle::mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t Indexsingle::mutex_update=PTHREAD_MUTEX_INITIALIZER;


Indexsingle::Indexsingle(){


	ifstream ifs2("./sample.lst");
	string line;
	while (std::getline(ifs2, line)){
		retrival_name.push_back(line_after_line(line));
		retrival_path.push_back(line_to_line(line));
	}
	ifs2.close();
	cout << "load sample.lst" << endl;
		
	int feat_start_pos = 2048; //###
	int feat_end_pos = 2560;
	int feat_width = feat_end_pos - feat_start_pos;
	string feature_file="./sample.feature";
	FILE *in1 = fopen(feature_file.c_str(), "rb");
	fread(&retrivalNum, sizeof(int), 1, in1);
	fread(&featDim, sizeof(int), 1, in1);
	cout<<"retrival num: "<<retrivalNum<<" feature dim: "<<featDim<<endl;

	index=faiss::IndexFlatIP(feat_width);
	cout<<"first:"<<&index<<endl;
	retrivalNum = retrival_name.size();
	Mat feat_retrival_add = Mat::zeros(retrivalNum, feat_width, CV_32FC1); //###
	for(int i = 0; i < retrivalNum; i++)
	{
		float *temp = new float[featDim];
		fread(temp, sizeof(float), featDim, in1);

		Mat dataRow(1, featDim, CV_32FC1, temp);
		feat_retrival.push_back(dataRow);
		dataRow = dataRow.colRange(feat_start_pos, feat_end_pos).clone(); //###
		float feat_norm = norm(dataRow);
		if(feat_norm != 0)
			dataRow = dataRow/feat_norm;
		dataRow.copyTo(feat_retrival_add.rowRange(i, i + 1));
		 
	delete []temp;
					
	}
	
	//float *featArr=new float[feat_retrival.rows*feat_retrival.cols];
	float* featArr=(float*)feat_retrival_add.data;
	index.add(retrivalNum,featArr);
	
	fclose(in1);
	cout << "load sample.feature" <<feat_retrival.size() <<endl;	
	cout << "load sample.lst" <<retrival_name.size() <<endl;	
	cout << "load index num:" <<index.ntotal <<endl;		
}	
	
boost::shared_ptr<Indexsingle> Indexsingle::initance(){
	if (p == NULL)
	{
		pthread_mutex_lock(&mutex);
		if (p == NULL)
			p =boost::shared_ptr<Indexsingle>(new Indexsingle);
		pthread_mutex_unlock(&mutex);
	}
	//cout<<"create:"<<&index<<endl;
	return p;
}

void  Indexsingle::update(string sampleFile,string featureFile){
	pthread_mutex_lock(&mutex_update);
	boost::shared_ptr<Indexsingle> temp=boost::shared_ptr<Indexsingle>(new Indexsingle);
	temp->appendFeature(sampleFile,featureFile);
	pthread_mutex_lock(&mutex);
	p=temp;
	pthread_mutex_unlock(&mutex);
	pthread_mutex_unlock(&mutex_update);
	//cout<<"create:"<<&index<<endl;
}

string Indexsingle::search(vector<bool> is_image,vector<string> query_name,  int queryNum, Mat feat_query,  float threshold){
//	cout<<"use:"<<p.use_count()<<endl;
 //cout<<feat_query<<endl;	
 cout<<"query index num:"<<index.ntotal<<endl;
	string result =faiss_retrival_return(index,is_image,query_name, retrival_name, queryNum, retrival_name.size(), feat_query, feat_retrival, 0.845);
	return result;
}

void Indexsingle::appendFeature(string sampleFile,string featureFile){
	ifstream ifs2(sampleFile);
	string line;
	
	int add_num=0;
	while (std::getline(ifs2, line)){
		retrival_name.push_back(line_after_line(line));
		retrival_path.push_back(line_to_line(line));
		add_num++;
	}
	ifs2.close();
	cout << "load sample.lst" << endl;
		
	int feat_start_pos = 2048; //###
	int feat_end_pos = 2560;
	int feat_width = feat_end_pos - feat_start_pos;
	int featDimTmp;
	//string feature_file="./sample.feature";
	FILE *in1 = fopen(featureFile.c_str(), "rb");
	int retrivalNumAdd;
	fread(&retrivalNumAdd, sizeof(int), 1, in1);
	fread(&featDimTmp, sizeof(int), 1, in1);
	cout<<"add lst num:"<<add_num<<endl;
	cout<<"add feature num:"<<retrivalNumAdd<<endl;
	if(add_num!=retrivalNumAdd){
		cout<<"not same num error!!!!"<<endl;
		return ;
	}
	retrivalNum+=retrivalNumAdd;
	cout<<"add retrival num: "<<retrivalNumAdd<<" feature dim: "<<featDimTmp<<endl;
	cout<<"retrival num: "<<retrivalNum<<" feature dim: "<<featDim<<endl;

	//index=faiss::IndexFlatIP(feat_width);
	//cout<<"first:"<<&index<<endl;
	retrivalNum = retrival_name.size();
	Mat feat_retrival_add = Mat::zeros(retrivalNumAdd, feat_width, CV_32FC1); //###
	for(int i = 0; i < retrivalNumAdd; i++)
	{
		float *temp = new float[featDim];
		fread(temp, sizeof(float), featDim, in1);

		Mat dataRow(1, featDim, CV_32FC1, temp);
		feat_retrival.push_back(dataRow);
		dataRow = dataRow.colRange(feat_start_pos, feat_end_pos).clone(); //###
		float feat_norm = norm(dataRow);
		if(feat_norm != 0)
			dataRow = dataRow/feat_norm;
		dataRow.copyTo(feat_retrival_add.rowRange(i, i + 1));
		 
	delete []temp;
					
	}
	//feat_retrival.push_back(feat_retrival_add);
	//float *featArr=new float[feat_retrival.rows*feat_retrival.cols];
	float* featArr=(float*)feat_retrival_add.data;
	index.add(retrivalNumAdd,featArr);
	
	fclose(in1);
	cout << "after add sample.feature" <<feat_retrival.size() <<endl;	
	cout << "after add sample.lst" <<retrival_name.size() <<endl;	
	cout << "after add index num:" <<index.ntotal <<endl;	
	
	
}
void Indexsingle::save(){
	pthread_mutex_lock(&mutex_update);
	ofstream ofile("./sample.lst.new");
	for(int i=0;i<retrival_name.size();i++){
		ofile<<retrival_path[i]<<" "<<retrival_name[i]<<endl;
	}
	ofile.close();
	rename("./sample.lst","./sample.lst.old");
	rename("./sample.lst.new","./sample.lst");
	cout<<"save list ok!"<<endl;
	
	FILE *output_file=fopen("./sample.feature.new", "wb");
	fwrite(&retrivalNum, sizeof(int), 1, output_file);
    fwrite(&featDim, sizeof(int), 1, output_file);
	fwrite((float*)feat_retrival.data, sizeof(float), retrivalNum * featDim, output_file);
	fclose(output_file);
	rename("./sample.feature","./sample.feature.old");
	rename("./sample.feature.new","./sample.feature");
	cout<<"save feature ok!"<<endl;
	cout<<"use:"<<p.use_count()<<endl;
	pthread_mutex_unlock(&mutex_update);
}

#endif
